#pragma once
#include "protocol.hpp"
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <string>
#include <functional>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/wait.h>
#include <unistd.h>
using namespace std;
static const uint16_t gport = 8080;
static const int gbacklog = 5;
using func_t = function<bool(const HttpRequest &, HttpResponse &)>;
enum
{
    USAGE_ERR = 1,
    SOCKET_ERR,
    BIND_ERR,
    LISTEN_ERR
};
class HttpServer
{
public:
    HttpServer(func_t func, const uint16_t &serverport)
        : _listensock(-1), _serverport(serverport), _func(func)
    {
    }
    void InitHttp()
    {
        _listensock = socket(AF_INET, SOCK_STREAM, 0);
        if (_listensock < 0)
        {
            exit(SOCKET_ERR);
        }
        int opt = 0;
        setsockopt(_listensock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_serverport);
        local.sin_addr.s_addr = htonl(INADDR_ANY);
        if (bind(_listensock, (struct sockaddr *)&local, sizeof(local)))
        {
            exit(BIND_ERR);
        }
        if (listen(_listensock, 5) < 0)
        {
            exit(LISTEN_ERR);
        }
    }
    void handlerhttp(int sock)
    {
        char buffer[4096];
        HttpRequest req;
        HttpResponse resp;
        int n = recv(sock, buffer, sizeof(buffer) - 1, 0);
        if (n > 0)
        {
            buffer[n] = 0;
            req.inbuffer += buffer;
            req.parse();
            _func(req, resp);
            send(sock, resp.outbuffer.c_str(), resp.outbuffer.size(), 0);
        }
    }
    void start()
    {
        for (;;)
        {
            struct sockaddr_in client;
            memset(&client, 0, sizeof(client));
            socklen_t len = sizeof(client);
            int sock = accept(_listensock, (struct sockaddr *)&client, &len);
            if (sock < 0)
            {
                continue;
            }
            pid_t id = fork();
            if (id == 0)
            {
                close(_listensock);
                if (fork() > 0)
                {
                    exit(0);
                }
                handlerhttp(sock);
                close(sock);
                exit(0);
            }
            close(sock);
            waitpid(id, nullptr, 0);
        }
    }
    ~HttpServer()
    {
    }

private:
    int _listensock;
    uint16_t _serverport;
    func_t _func;
};